<?php
// +----------------------------------------------------------------------
// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------
// | Author: Dean <zxxjjforever@163.com>
// +----------------------------------------------------------------------
namespace cmf\controller;

use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Request;
use think\Config;
use think\Response;
use think\Loader;
use think\Db;

class RestBaseController
{

    //token
    protected $token = '';

    //设备类型
    protected $deviceType = '';

    protected $apiVersion;

    protected $allowedDeviceTypes = ['mobile', 'android', 'iphone', 'ipad', 'web', 'pc', 'mac', 'wxapp'];

    protected $userLevel = [
        1=> 'one',//环保志愿者0星
        2=> 'two',//环保志愿者1星
        3=>'three',//环保志愿者2星
        4=>'fore',//环保志愿者3星
        5=>'five',//环保志愿者4星
        6=>'six',//环保志愿者5星
    ];

    protected $bankName = [
        "中国农业银行"=> 'abc',//中国农业银行
        "中国工商银行"=> 'icbc',//中国工商银行
        "中国建设银行"=>'ccb',//中国建设银行
        "中国银行"=>'boc',//中国银行
    ];

    public $moneyType = [
        1=>'mrhb',
        2=>'exchange',
        3=>'tochangeinto',
        4=>'rollout',
        11=>'jymr',
        12=>'jymc',
        13=>'qxjyhk',
        21=>'mrbzjkc',
        22=>'mrbzjfh',
        23=>'mcbzjkc',
        24=>'mcbzjfh',
        31=>'scfk',
        32=>'scsk',
        41=>'xtbd',
        51=>'ztjl',
        52=>'qkjl',
        53=>'ltjl',
        69=>'ccsxf'
    ];

    public $scoresType = [
        1=>'mrhb',
        2=>'jfdh',
        3=>'zrzs',
        4=>'zczs',
        5=>'tjzc',
        6=>'zc',
        31=>'scfk',
        32=>'scsk',
        42=>'xtbd',
        51=>'ztjl',
        52=>'qkjl',
        53=>'ltjl',
        54=>'gjhyjf',
        55=>'djhyjf',
        61=>'qukuai',
        62=>'bole',
        63=>'partner',
        66=>'bagnon'
    ];
    /**
     * 190622 hcq 增加接口日志
     * @var string 请求ID
     */
    protected $req_id;

    /**
     * 190622 hcq 增加接口日志
     * @var array 日志内容
     */
    protected $log_content = [];

    /**
     * @var \think\Request Request实例
     */
    protected $request;
    // 验证失败是否抛出异常
    protected $failException = false;
    // 是否批量验证
    protected $batchValidate = false;

    /**
     * 前置操作方法列表
     * @var array $beforeActionList
     * @access protected
     */
    protected $beforeActionList = [];

    /**
     * 架构函数
     * @param Request $request Request对象
     * @access public
     */
    public function __construct(Request $request = null)
    {
        if (is_null($request)) {
            $request = Request::instance();
        }

        Request::instance()->root(cmf_get_root() . '/');

        $this->request = $request;

        $this->apiVersion = $this->request->header('XX-Api-Version');

        /* edit by  190622: 接口日志增加请求ID */
        $this->req_id = qbt_guid();
        $this->log_content = [
            'req_id'=>$this->req_id,
            'req_url'=>$this->request->url(),
            'req_time'=>date('Y-m-d H:i:s'),
            'req_method'=>$this->request->method(),
            'ip'=>get_client_ip(),
            'ua'=>$_SERVER['HTTP_USER_AGENT'],
        ];

        // 控制器初始化
        $this->initialize();
        // 老的控制器初始化 即将取消
        $this->_initialize();

        // 前置操作方法
        if ($this->beforeActionList) {
            foreach ($this->beforeActionList as $method => $options) {
                is_numeric($method) ?
                    $this->beforeAction($options) :
                    $this->beforeAction($method, $options);
            }
        }

    }
    protected function is_open(){
        $stat = Db::name('config')->where('id',1)->find();
        if(isset($stat['value']) && $stat['value'] ==1){
            $arr = explode('-', $stat['options']);
            $now = strtotime(date('H:i:s'));
            if($now >= strtotime($arr[0]) && $now<= strtotime($arr[1])){
                $this->error($stat['tip']);//网站维护中
            }
        }
    }
    // 初始化
    protected function initialize(){

        $this->is_open();
        /* edit by  181023: 接口签名校验程序 */
        $data = $this->request->param();

        /* edit by  190622: 增加接口日志记录 */
        $this->log_content['req_content'] = json_encode($data, JSON_UNESCAPED_UNICODE);

        $this->saveApiLog();

        if (!isset($data['sign']) || empty($data['sign'])) {
         //   $this->error('签名不能为空');
        } else {
            $old_sign = $data['sign'];
            unset($data['sign']);
            $sign = $this->getSign($data);
           if ($sign != $old_sign) {
              // $this->error("签名不正确！");
            }
        }

    }
    
    // 初始化
    protected function _initialize()
    {
    }

    /**
     * 格式化参数，签名过程需要使用
     * add by  181023
     * @access protected
     * @param array $paraMap 格式化数组
     * @param bool $urlencode 是否需要URL编码
     * @return string 排列参数顺序字符串
     */
    protected function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

    /**
     * 生成签名
     * add by  181023
     * @access protected
     * @param array $parm 签名参数
     * @return string 签名信息
     */
    protected function getSign($parm)
    {
        $parm['key'] = config('cmf_api_key');
        $parm['key'] = 'abc';
        $sign_string = $this->formatBizQueryParaMap($parm, false);

        /* edit by  190622 修复复杂接口提交数据被转义导致加密MD5验证不一致问题 */
        $sign_string = htmlspecialchars_decode($sign_string);

        $sign = strtolower(md5($sign_string));

        /* edit by  190622: 增加接口日志记录 */
        $this->log_content['sign_string'] = $sign_string;
        $this->log_content['sign_md5'] = $sign;

        $this->saveApiLog();

        //签名步骤三：MD5加密
        return $sign;
    }

    /**
     * 前置操作
     * @access protected
     * @param string $method 前置操作方法名
     * @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]]
     */
    protected function beforeAction($method, $options = [])
    {
        if (isset($options['only'])) {
            if (is_string($options['only'])) {
                $options['only'] = explode(',', $options['only']);
            }
            if (!in_array($this->request->action(), $options['only'])) {
                return;
            }
        } elseif (isset($options['except'])) {
            if (is_string($options['except'])) {
                $options['except'] = explode(',', $options['except']);
            }
            if (in_array($this->request->action(), $options['except'])) {
                return;
            }
        }

        call_user_func([$this, $method]);
    }


    /**
     * 设置验证失败后是否抛出异常
     * @access protected
     * @param bool $fail 是否抛出异常
     * @return $this
     */
    protected function validateFailException($fail = true)
    {
        $this->failException = $fail;
        return $this;
    }

    /**
     * 验证数据
     * @access protected
     * @param array $data 数据
     * @param string|array $validate 验证器名或者验证规则数组
     * @param array $message 提示信息
     * @param bool $batch 是否批量验证
     * @param mixed $callback 回调方法（闭包）
     * @return array|string|true
     * @throws ValidateException
     */
    protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
    {
        if (is_array($validate)) {
            $v = Loader::validate();
            $v->rule($validate);
        } else {
            if (strpos($validate, '.')) {
                // 支持场景
                list($validate, $scene) = explode('.', $validate);
            }
            $v = Loader::validate($validate);
            if (!empty($scene)) {
                $v->scene($scene);
            }
        }
        // 是否批量验证
        if ($batch || $this->batchValidate) {
            $v->batch(true);
        }

        if (is_array($message)) {
            $v->message($message);
        }

        if ($callback && is_callable($callback)) {
            call_user_func_array($callback, [$v, &$data]);
        }

        if (!$v->check($data)) {
            if ($this->failException) {
                throw new ValidateException($v->getError());
            } else {
                return $v->getError();
            }
        } else {
            return true;
        }
    }

    /**
     * 操作成功跳转的快捷方法
     * @access protected
     * @param mixed $msg 提示信息
     * @param mixed $data 返回的数据
     * @param array $header 发送的Header信息
     * @return void
     */
    protected function success($msg = '', $data = NULL, array $header = [])
    {
        $code   = 1;
        $result = [
            'req_id' => $this->req_id,
            'code' => $code,
            'msg'  => $msg,
            'data' => $data,
        ];

        /* 190624 hcq 增加日志记录 */
        $this->log_content['res_time'] = date('Y-m-d H:i:s');
        $this->log_content['res_content'] = json_encode($result, JSON_UNESCAPED_UNICODE);
        $this->log_content['status'] = 'success';
        $this->saveApiLog();

        $type                                   = $this->getResponseType();
        $header['Access-Control-Allow-Origin']  = '*';
        $header['Access-Control-Allow-Headers'] = 'X-Requested-With,Content-Type,XX-Device-Type,XX-Token,XX-Api-Version,XX-Wxapp-AppId';
        $header['Access-Control-Allow-Methods'] = 'GET,POST,PATCH,PUT,DELETE,OPTIONS';
        $response                               = Response::create($result, $type)->header($header);
        throw new HttpResponseException($response);
    }

    /**
     * 操作错误跳转的快捷方法
     * @access protected
     * @param mixed $msg 提示信息,若要指定错误码,可以传数组,格式为['code'=>您的错误码,'msg'=>'您的错误消息']
     * @param mixed $data 返回的数据
     * @param array $header 发送的Header信息
     * @return void
     */
    protected function error($msg = '', $data = NULL, array $header = [])
    {
        $code = 0;
        if (is_array($msg)) {
            $code = $msg['code'];
            $msg  = $msg['msg'];
        }
        $result = [
            'req_id' => $this->req_id,
            'code' => $code,
            'msg'  => $msg,
            'data' => $data,
        ];

        /* 190624 hcq 增加日志记录 */
        $this->log_content['res_time'] = date('Y-m-d H:i:s');
        $this->log_content['res_content'] = json_encode($result, JSON_UNESCAPED_UNICODE);
        $this->log_content['status'] = 'error';
        $this->saveApiLog();

        $type                                   = $this->getResponseType();
        $header['Access-Control-Allow-Origin']  = '*';
        $header['Access-Control-Allow-Headers'] = 'X-Requested-With,Content-Type,XX-Device-Type,XX-Token';
        $header['Access-Control-Allow-Methods'] = 'GET,POST,PATCH,PUT,DELETE,OPTIONS';
        $response                               = Response::create($result, $type)->header($header);
        throw new HttpResponseException($response);
    }

    /**
     * 获取当前的response 输出类型
     * @access protected
     * @return string
     */
    protected function getResponseType()
    {
        return 'json';
    }

    /**
     * 190624 hcq 增加日志写入
     * 写入日志方法
     */
    protected function saveApiLog(){
        if(config('cmf_api_log')){
            //延迟写入SQL，避免数据太多需要等待处理造成接口反馈慢的问题
            $_sql = Db::name('api_logs')->fetchSql()->insert($this->log_content,true);
            $_sql = str_replace('REPLACE INTO','REPLACE DELAYED INTO',$_sql);
            Db::execute($_sql);
        }
    }
}